package com.zhy.system.filter;


import com.fasterxml.jackson.databind.ObjectMapper;

import com.zhy.common.helper.JwtHelper;
import com.zhy.common.result.Result;
import com.zhy.common.util.IpUtil;
import com.zhy.common.util.ResponseUtil;
import com.zhy.model.system.SysUser;
import com.zhy.model.vo.LoginVo;
import com.zhy.system.custom.CustomUser;
import com.zhy.system.service.LoginLogService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义过滤器实现认证过程
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;

    private LoginLogService loginLogService;

    public TokenLoginFilter(AuthenticationManager authenticationManager , RedisTemplate redisTemplate , LoginLogService loginLogService) {
        //设置认证管理器
        this.setAuthenticationManager(authenticationManager);
        //设置登录的请求地址和请求方式
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login", "POST"));
        //给redisTemplate属性赋值
        this.redisTemplate = redisTemplate;
        //给loginLogService属性赋值
        this.loginLogService = loginLogService;
    }

    //认证的方法
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            //获取前端输入的用户名和密码
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            //创建未认证的Authentication对象
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            //委托认证管理器进行认证
            return this.getAuthenticationManager().authenticate(authRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //认证成功的方法
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //获取CustomUser对象
        CustomUser customUser = (CustomUser) authResult.getPrincipal();
        //获取SysUser对象
        SysUser sysUser = customUser.getSysUser();
        //通过JwtHelper生存token
        String token = JwtHelper.createToken(sysUser.getId(), sysUser.getUsername());
        //创建一个Map
        Map map = new HashMap<>();
        map.put("token",token);

        //获取用户的权限
        Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
        //将用户的权限放到Redis
        redisTemplate.boundValueOps(sysUser.getUsername()).set(authorities);

        //插入登录日志
        //获取用户名
        String username = sysUser.getUsername();
        //获取ip地址
        String ipAddress = IpUtil.getIpAddress(request);
        //调用LoginLogService中记录登录日志的方法
        loginLogService.recordLoginLog(username,ipAddress,1,"登录成功");

        //通过ResponseUtil工具类给前端响应
        ResponseUtil.out(response, Result.ok(map));
    }

    //认证失败的方法
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        //通过ResponseUtil工具类给前端响应
        ResponseUtil.out(response,Result.build(null,444,failed.getMessage()));
    }
}
